/* -*-c++-*-
 * This source code is proprietary of ADIT
 * Copyright (C) 2013 Advanced Driver Information Technology Joint Venture GmbH
 * All rights reserved
 *
 * Author: Vadiraj Kaamsha <vadiraj.kaamsha@in.bosch.com>
 * Author: Rudolf Dederer <rudolf.dederer@de.bosch.com>
*/

#ifndef OSGBATCHEDTEXT_BATCHDRAWABLEBASE
#define OSGBATCHEDTEXT_BATCHDRAWABLEBASE 1

#include <osg/Drawable>
#include <osgBatchedText/VecArray>
#include <osgBatchedText/Export>

namespace osgBatchedText {

class BatcherBase;
class BatchElementContainer;

struct OSGBATCHEDTEXT_EXPORT BatchedVertexAttributes
{
   osg::ref_ptr<ArrayBase> _vertices; // contains Vertex list + max vis scale level which is used for alpha calculations in shader.

   std::vector<osg::Vec4> _colorArray;
   std::vector<osg::Vec3> _texCoord;

   /**
      * _transVecCharRatioAttrib is used to send per vertex Attributes as follows
      *
      * [x] -> Translation x component.
      * [y] -> Translation y component.
      * [z] -> Translation z component.
      * [w] -> Flag indicating presence of outline
      */
   std::vector<osg::Vec4> _transVecAttribOutline;
   /** contains relative offset of character position to the first character */
   std::vector<osg::Vec3> _relOffset;

   void init(bool useHalfFloat);

   VecArray<osg::Vec4h>* getVertexArrayHalfFloat() const { return static_cast<VecArray<osg::Vec4h>*>(_vertices.get()); } //lint !e1774 //Array type controlled based on whether or not using half-float
   VecArray<osg::Vec4f>* getVertexArrayFloat() const { return static_cast<VecArray<osg::Vec4f>*>(_vertices.get()); } //lint !e1774 //Array type controlled based on whether or not using half-float

   void clear();
   void reserve(unsigned int num);
   void resize(unsigned int num);
};



class OSGBATCHEDTEXT_EXPORT BatchDrawableBase : public osg::Drawable
{
public:
   enum tShaderType
   {
      STRAIGHT_ORTHO,        // straight text with orthographic matrix for drawing text
      STRAIGHT_SCREEN,       // straight text screen aligned
      STRAIGHT_GROUND,       // straight text on the ground
      STRAIGHT_GROUND_FIXED, // straight text on the ground not rotating
                             // ----------------------------------------------
                             // from CURVED_SCREEN enum value should be only and all values of "curved text" type
                             // as we use this value for decision
      CURVED_SCREEN,         // characters placed along street base line, but billboarded. Chinese use case.
      CURVED_GROUND,         // characters put along the base line
      CURVED_GROUND_FIXED    // characters put along the base line not rotating
                             //      with pitch angle 0 they are lying on the ground
                             //      with pitch angle PI/2 they are standing upright, facing street
   };
   static bool isCurvedText(tShaderType shaderType) { return (shaderType >= CURVED_SCREEN); }

   BatchDrawableBase(BatcherBase* parent, tShaderType shaderType, bool enableDepthTest = true);
   BatchDrawableBase(const BatchDrawableBase& src, const osg::CopyOp& copyop);

   void init(bool useHalfFloat);

   virtual osg::Object* cloneType() const { return new BatchDrawableBase(0, STRAIGHT_SCREEN, true); }
   virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new BatchDrawableBase(*this, copyop); }
   virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast<const BatchDrawableBase*>(obj) != NULL; } // dynamic cast used as the function is used to determine the data-type
   virtual const char* className() const { return "TextBatchDrawable"; }
   virtual const char* libraryName() const { return "types"; }


   /** Set whether to use a mutex to ensure ref() and unref() are thread safe.*/
   virtual void setThreadSafeRefUnref(bool threadSafe);

   /** Resize any per context GLObject buffers to specified size. */
   virtual void resizeGLObjectBuffers(unsigned int maxSize);

   /** If State is non-zero, this function releases OpenGL objects for
     * the specified graphics context. Otherwise, releases OpenGL objects
     * for all graphics contexts. */
   virtual void releaseGLObjects(osg::State* state = 0) const;

   /** Draw the text.*/
   virtual void drawImplementation(osg::RenderInfo& renderInfo) const;

   /** batched text does not support accept(AttributeFunctor&).*/
   virtual bool supports(const osg::Drawable::AttributeFunctor&) const { return false; }

   /** batched text supports accept(ConstAttributeFunctor&).*/
   virtual bool supports(const osg::Drawable::ConstAttributeFunctor&) const { return true; }

   /** batched text supports accept(PrimitiveFunctor&) */
   virtual bool supports(const osg::PrimitiveFunctor&) const { return true; }

   /** accept method for ConstAttributeFunctor */
   virtual void accept(osg::Drawable::ConstAttributeFunctor& af) const;

   /** accept method for PrimitiveFunctor */
   virtual void accept(osg::PrimitiveFunctor& pf) const;

   virtual bool checkAndSetTexAttribute(const osg::ref_ptr<osg::Texture>& /*texture*/, osg::State& /*state*/, int& /*textureUnit*/) const { return true; }

   virtual bool prepareVertextAttributes(osg::State& state, std::vector< osg::ref_ptr<BatchElementContainer> >::const_iterator& elemItr) const;

   void draw(osg::State &state) const;

   virtual void reserveVertexAttribMemory() const;

   virtual bool isInPickingMode() const { return false; }

   void sortListForDrawing() const;

   tShaderType getShaderType() const { return _shaderType; }
   bool enableDepthTest() const { return _enableDepthTest; }

   unsigned int getNumberOfVertices() const;

   void addBatchElementContainer(const osg::ref_ptr<BatchElementContainer>& batchElementContainer, bool update = true);


   void clearElementsToDraw() const { _lstBatchElementContainer.clear(); _setBatchElementContainer.clear(); }

   unsigned int getNumberOfBatchedElements() { return _lstBatchElementContainer.size(); }

   static const GLint _transVecOutlineAttribLoc = 7;
   static const GLint _relOffset = 5;

   static void bindAttribLocations(osg::Program* shaderProgram);

protected:

   virtual ~BatchDrawableBase() { _parent = NULL; }

   void calculateTransVec(const osg::Matrix& mvMat) const;

   const BatcherBase* _parent;

   tShaderType _shaderType;

   bool _enableDepthTest;

   mutable BatchedVertexAttributes _vertAttrib;

   mutable std::vector< osg::ref_ptr<BatchElementContainer> > _lstBatchElementContainer;


   struct batchElementContainerPtrCmp
   {
      bool operator() (const BatchElementContainer* const& lhs, const BatchElementContainer* const& rhs) const;
   };


   mutable std::set< BatchElementContainer*, batchElementContainerPtrCmp > _setBatchElementContainer;
};


struct OSGBATCHEDTEXT_EXPORT BatchDrawParams
{
   BatchDrawParams(const BatchDrawableBase* batchDrawable, osg::State* state)
      : _batchDrawable(batchDrawable), _state(state), _countRetry(0)  { }

   const BatchDrawableBase* _batchDrawable;

   osg::State* _state;

   // tbd calculate vertices in dependency of ContextID->support for different views
   unsigned int _countRetry;
};

}

#endif //OSGBATCHEDTEXT_BATCHDRAWABLEBASE
